package ils.services;

import ils.domain.catalog.model.Book;
import ils.domain.catalog.model.BookRepository;
import ils.domain.issue.model.*;
import ils.util.HibernateSession;
import ils.util.HibernateSessionManager;
import ils.util.Util;
import org.hibernate.SessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.transaction.annotation.Transactional;

import javax.jms.*;
import java.util.Date;
import java.util.List;



public class IssueServiceImpl implements IssueService
{
    private ReserveRepository reserveRepository;
    private BookRepository bookRepository;
    private ItemRepository itemRepository;
    private IssueRepository issueRepository;
    private static final Logger logger = LoggerFactory.getLogger(IssueService.class);
    private SessionFactory sessionFactory;
    private Queue bookRelease;
    private JmsTemplate jmsTemplate;
    private HibernateSessionManager hibernateSessionManager;
    private ShelfRepository shelfRepository;

    @Override
    public Reserve request(int bookId, int userId)
    {
        try(HibernateSession session = hibernateSessionManager.getSession())
        {
            Reserve reserve = new Reserve();
            reserve.setUserId(userId);
            reserve.setBookId(bookId);
            reserve.setReserveDate(new Date());
            reserve.setExpiryDate(new Date(System.currentTimeMillis() + Util.DAY * 7));
            return reserveRepository.createReserve(reserve);
        }
    }

    @Override
    public Issue issue(String barcode, int userId)
    {
        try(HibernateSession session = hibernateSessionManager.getSession())
        {
            Issue issue = new Issue();
            issue.setUserId(userId);
            Item item = itemRepository.getItem(barcode);
            if(!item.getStatus().equals("active"))
            {
                sessionFactory.getCurrentSession().getTransaction().rollback();
                throw new RuntimeException("The book is not available");
            }
            issue.setItem(item);
            issue.setIssueDate(new Date());
            issue.setDueDate(new Date(System.currentTimeMillis() + Util.DAY * 14));
            issueRepository.createIssue(issue);
            Book book = bookRepository.getBook(item.getBookId());

            // decrement avaialble count
            int count = 0;
            if(book.getAvailableCount() != null) count = book.getAvailableCount();
            book.setAvailableCount(count - 1);
            // change the status of the item
            item.setStatus("onRent");
            // if a reserveration exist for the user for this book, change status of that
            Reserve reserve = reserveRepository.getReserve(userId, book.getId());
            if(reserve != null)
            {
                reserve.setStatus(true);
            }
            return issue;
        }
    }

    @Override
    public void returnBack(int bookId, int userId)
    {
        logger.debug("returned book: " + bookId + " by user: " + userId);

    }

    @Override
    public void returnBack(String barcode, int userId)
    {
        // TODO Auto-generated method stub

    }

    @Override
    @Transactional
    public Issue release(String barcode, int userId)
    {
        try(HibernateSession session = hibernateSessionManager.getSession())
        {
            Issue issue = issueRepository.getIssue(barcode);
            issue.setReturnDate(new Date());
            Item item = itemRepository.getItem(barcode);
            Book book = bookRepository.getBook(item.getBookId());
            int count = book.getAvailableCount();
            book.setAvailableCount(count + 1);
            item.setStatus("active");
            sendMessage(book.getId(), userId);
            return issue;
        }
    }
    private void sendMessage(final int bookId, final int userId)
    {
        MessageCreator messageCreator = new MessageCreator()
        {   
            @Override
            public Message createMessage(Session session) throws JMSException
            {
                MapMessage message = session.createMapMessage();
                message.setInt("bookId", bookId);
                message.setInt("userId", userId);
                return message;
            }
        };
        jmsTemplate.send(bookRelease, messageCreator);
    }

    @Override
    public List<Issue> getAUsersOpenIssues(int userId)
    {
        try(HibernateSession session = hibernateSessionManager.getSession())
        {
            return issueRepository.getOpenIssuesForUser(userId);
        }
    }

    @Override
    public List<Issue> getIssuesForABook(int bookId, int firstResult, int count) {
        try(HibernateSession session = hibernateSessionManager.getSession())
        {
            return issueRepository.getIssuesForABook(bookId, firstResult, count);
        }
    }

    public void setReserveRepository(ReserveRepository reserveRepository)
    {
        this.reserveRepository = reserveRepository;
    }

    @Override
    public List<Reserve> getReservations(int firstResult)
    {
        try(HibernateSession session = hibernateSessionManager.getSession())
        {
            return reserveRepository.getReservations(firstResult, RESERVES_PAGE_SIZE);
        }
    }

    public void setBookRepository(BookRepository bookRepository)
    {
        this.bookRepository = bookRepository;
    }

    public void setItemRepository(ItemRepository itemRepository)
    {
        this.itemRepository = itemRepository;
    }

    public void setIssueRepository(IssueRepository issueRepository)
    {
        this.issueRepository = issueRepository;
    }

    @Override
    public List<Issue> getOpenIssues(int firstResult)
    {
        try(HibernateSession session = hibernateSessionManager.getSession())
        {
            return issueRepository.getOpenIssues(firstResult, ISSUES_PAGE_SIZE);
        }
    }

    public void setSessionFactory(SessionFactory sessionFactory)
    {
        this.sessionFactory = sessionFactory;
    }

    public void setBookRelease(Queue bookRelease)
    {
        this.bookRelease = bookRelease;
    }

    public void setJmsTemplate(JmsTemplate jmsTemplate)
    {
        this.jmsTemplate = jmsTemplate;
    }

    @Override
    public List<Reserve> getReservationsForUser(int userId)
    {
        try(HibernateSession session = hibernateSessionManager.getSession())
        {
            return reserveRepository.getReservationsForUser(userId);
        }
    }

    @Override
    public Item addItem(Item item, int shelfId) {
        try(HibernateSession session = hibernateSessionManager.getSession())
        {
            Shelf shelf = shelfRepository.getShelf(shelfId);
            item.setShelf(shelf);
            return itemRepository.createItem(item);
        }
    }

    public void setHibernateSessionManager(HibernateSessionManager hibernateSessionManager) {
        this.hibernateSessionManager = hibernateSessionManager;
    }

    public void setShelfRepository(ShelfRepository shelfRepository) {
        this.shelfRepository = shelfRepository;
    }
}
